#include "GameplayController.h"

#include "windows.h"
#include <iostream>

#include <boost/bind.hpp>
#include "../../domain/configuration/CardsConfiguration.h"
#include "../../utils/LateEvents.hpp"
#include "../../utils/logging/GraphicLogger.h"

using namespace Controllers;

GameplayController::GameplayController(Utils::GUI::Form* form)
	: form(form), winner(NULL), view(NULL), turn_controller(NULL)
{
	Domain::Configuration::CardsConfiguration cards_configuration;
	Cards::PlayersCards players_cards = cards_configuration.createPlayersCards();
	Cards::WeaponsCards weapons_cards = cards_configuration.createWeaponsCards();
	Cards::RoomsCards rooms_cards = cards_configuration.createRoomsCards();
	cards = Cards::CardsCollection(players_cards, weapons_cards, rooms_cards);
}

GameplayController::~GameplayController()
{
	delete view;
	delete turn_controller;
	cards.clean();
}

void GameplayController::startGame()
{
	setEvents();
	pickPlayers();
}

void GameplayController::setEvents()
{
	connectEvent<void ()>(playersSetEvent, boost::bind(&GameplayController::dealCards, this));
	connectEvent<void ()>(cardsDealEvent, boost::bind(&GameplayController::startRound, this));
}

void GameplayController::pickPlayers()
{
	connectEvent<void (std::queue<std::pair<Cards::PlayerCard*, Players::PLAYER_TYPE>>)>(
		view->playersPickEvent, boost::bind(&GameplayController::onPlayersPicked, this, _1));
	view->openPickPlayersDialog(cards.getPlayersCards());
}

void GameplayController::onPlayersPicked(std::queue<std::pair<Cards::PlayerCard*, Players::PLAYER_TYPE>> p_cards)
{
	while(!p_cards.empty())
	{
		players.push(createPlayer(p_cards.front().first, p_cards.front().second));
		p_cards.pop();
	}
	starting_player = current_player = players.front();
	setLogger();
	logger->gameplayStartGame();
	playersSetEvent();
}

Players::Player* GameplayController::createPlayer(Cards::PlayerCard* card, Players::PLAYER_TYPE type)
{
	return new Players::Player(card->getTitle(), card->getPiece(), type);
}

void GameplayController::startRound()
{
	startTurn();
}

void GameplayController::startTurn()
{
	delete turn_controller;
	turn_controller = 0;

	logger->gameplayStartTurn(current_player);
	view->startTurn(current_player);
	turn_controller = createTurnController();
	connectEvent<void ()>(turn_controller->turnEndEvent, boost::bind(&GameplayController::endTurn, this));
	connectEvent<void ()>(turn_controller->playerWinEvent, boost::bind(&GameplayController::onGameWin, this));
	connectEvent<void ()>(turn_controller->playerLoseEvent, boost::bind(&GameplayController::excludePlayer, this));
	turn_controller->begin();
}

void GameplayController::nextPlayer()
{
	players.pop();
	players.push(current_player);
	current_player = players.front();
}

void GameplayController::endTurn()
{
	view->endTurn(current_player);
	if (hasMoreThanOneActivePlayer())
	{
		do
		{
			nextPlayer();
		}
		while(!current_player->isActive());

		if (current_player == starting_player)
			Utils::AppLoopEvent(boost::bind(&GameplayController::endRound, this));
		else
			Utils::AppLoopEvent(boost::bind(&GameplayController::startTurn, this));
	}
	else
	{
		winner = findActivePlayer();
		endGame(Domain::Configuration::GAME_END_COMPETITORS_LOST);
	}
}

void GameplayController::endRound()
{
	OutputDebugStringA("Koniec rundy\n");
	startRound();
}

void GameplayController::onGameWin()
{
	winner = current_player;
	connectEvent<void ()>(view->playerWinDialogCloseEvent,
		boost::bind(&GameplayController::onPlayerWinDialogClose, this));
	view->playerWin(current_player);
}

void GameplayController::excludePlayer()
{
	current_player->setInactive();
	connectEvent<void ()>(view->playerLoseDialogCloseEvent,
		boost::bind(&GameplayController::onPlayerLoseDialogClose, this));
	logger->gameplayPlayerLose();
	view->playerLose(current_player);
}

bool GameplayController::isGameEnded()
{
	return winner != NULL;
}

bool GameplayController::hasMoreThanOneActivePlayer()
{
	Players::PlayersQueue plrs = players;
	int active_players_count = 0;
	while(!plrs.empty())
	{
		if (plrs.front()->isActive())
			active_players_count++;
		plrs.pop();
	}
	return active_players_count > 1;
}

Players::Player* GameplayController::findActivePlayer()
{
	Players::PlayersQueue plrs = players;
	while(!plrs.empty())
	{
		if (plrs.front()->isActive())
			return plrs.front();
		plrs.pop();
	}
	return NULL;
}

void GameplayController::onPlayerWinDialogClose()
{
	endGame(Domain::Configuration::GAME_END_DEDUCTED);
}

void GameplayController::onPlayerLoseDialogClose()
{
	endTurn();
}

void GameplayController::endGame(Domain::Configuration::GAME_END_REASON reason)
{
	if (winner == current_player)
	{
		gameWonEvent(winner, searched_cards, reason);
	}
	else
	{
		gameLostEvent(winner, searched_cards, reason);
	}
}

void GameplayController::setLogger()
{
	Utils::Logging::GraphicLogger* graphic_logger = new Utils::Logging::GraphicLogger(form);
	graphic_logger->Move(0, form->GetHeight() - graphic_logger->GetHeight());
	logger = graphic_logger;
}

